/*
 * tclass.c
 *
 *  Created on: Jan 24, 2016
 *      Author: root
 */

#include <string.h>
#include <stdio.h>
#include "../basic/operator.h"



//TODO
// -- (define Class Circle (radias Number) (make-circle) (draw move area))
// -- (define Method area Circle () (* *PI* radias radias))
// -- (define Constructor make-circle Circle (radias) (set! (this radias) radias))
// -- (define Procedure add5 (lambda (x) (+ x 5)))
// -- (define Procedure (add6 x) (+ x 6))
// -- (define Circle circle (make-circle 10))
// (define add5 (lambda (x) (+ x 5)))
// (define (add6 x) (+ x 6))
// (define circle (make-circle 10))

/*
Basic types hierarchy:

-SystemObject
  -Class
  -Atom
    -Boolean
    -Number
      -Real
        -Decimal
        -Fraction
        -Integer
      -Complex
    -Character
    -String
    -Symbol
      -Syntax
  	  -Property
  -Vector
  -Environment
  -Function
    -Procedure
    -Closure
    -Continuation
    -Method
    -Constructor
  -Exception
  -Pair
    -Promise
 * */

void init_class_hierarchy(Scheme *sc) {
	sc->inheritance = init_tree();
	tree_add(sc->inheritance, NULL, OBJECT_TYPE, G_NIL);
	tree_add(sc->inheritance, OBJECT_TYPE, SYSTEM_OBJECT, G_NIL);

    tree_add(sc->inheritance, SYSTEM_OBJECT, CLASS_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, ATOM_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, VECTOR_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, ENVIRONMENT_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, FUNCTION_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, EXCEPTION_TYPE, G_NIL);
    tree_add(sc->inheritance, SYSTEM_OBJECT, PAIR_TYPE, G_NIL);

    tree_add(sc->inheritance, ATOM_TYPE, BOOLEAN_TYPE, G_NIL);
    tree_add(sc->inheritance, ATOM_TYPE, NUMBER_TYPE, G_NIL);
    tree_add(sc->inheritance, ATOM_TYPE, CHARACTER_TYPE, G_NIL);
    tree_add(sc->inheritance, ATOM_TYPE, STRING_TYPE, G_NIL);
    tree_add(sc->inheritance, ATOM_TYPE, SYMBOL_TYPE, G_NIL);

    tree_add(sc->inheritance, NUMBER_TYPE, COMPLEX_TYPE, G_NIL);
    tree_add(sc->inheritance, NUMBER_TYPE, REAL_TYPE, G_NIL);
    tree_add(sc->inheritance, REAL_TYPE, DECIMAL_TYPE, G_NIL);
    tree_add(sc->inheritance, REAL_TYPE, FRACTION_TYPE, G_NIL);
    tree_add(sc->inheritance, REAL_TYPE, INTEGER_TYPE, G_NIL);

    tree_add(sc->inheritance, SYMBOL_TYPE, SYNTAX_TYPE, G_NIL);

	tree_add(sc->inheritance, FUNCTION_TYPE, PROCEDURE_TYPE, G_NIL);
    tree_add(sc->inheritance, FUNCTION_TYPE, CLOSURE_TYPE, G_NIL);
    tree_add(sc->inheritance, FUNCTION_TYPE, CONTINUATION_TYPE, G_NIL);
    tree_add(sc->inheritance, FUNCTION_TYPE, METHOD_TYPE, G_NIL);

    tree_add(sc->inheritance, PAIR_TYPE, PROMISE_TYPE, G_NIL);
}

Cell *make_class(Scheme *sc, char *fullClassName) {
	Cell *vector = make_vector(sc, CLASS_ITEMS_SIZE);
	vector->_type = CLASS_TYPE;
	tree_add(sc->inheritance, OBJECT_TYPE, fullClassName, G_NIL);
	return vector;
}

// (define-class demo/poly/Animal)
Cell* op_define_class(Scheme *sc) {
	char *fullClassName = first(sc->args)->_name;
	Cell *class = make_class(sc, fullClassName);
	define_global_constant(sc, fullClassName, class);
    sc->returnValue = G_TRUE;
    return pop_callstack(sc);
}

// (add-inheritance demo/poly/Circle demo/poly/Shape)
Cell* op_add_inheritance(Scheme *sc) {
	char *subClass = first(sc->args)->_name;
	char *superClass = second(sc->args)->_name;
	tree_add(sc->inheritance, superClass, subClass, G_NIL);
    sc->returnValue = G_TRUE;
    return pop_callstack(sc);
}

// (add-property demo/poly/Circle radius)
Cell* op_add_property(Scheme *sc) {
	char *className = first(sc->args)->_name;
    char *propertyName = second(sc->args)->_name;
	Cell* class = lookup_variable_value(sc, sc->globalEnv, className);
    if (type_of(sc, class, EXCEPTION_TYPE)) {
        sc->returnValue = class;
        return pop_callstack(sc);
    }

    int location = hash_fn(propertyName, class->_longValue);
    Cell* item = get_vector_item(class, location);
    Cell* slot = NULL;
    for (; item != G_NIL; item = cdr(item)) {
        slot = car(item);
        if (strcmp(car(slot)->_name, propertyName) == 0) {
            sc->returnValue = make_default_exception(sc, "PROPERTY_OR_METHOD_EXISTED", "Property or method already existed!");
            return pop_callstack(sc);
        }
    }

    Cell* variable = make_variable(sc, propertyName);
    slot = immutable_cons(sc, variable, G_NIL); // init property value as nil
    set_vector_item(class, location, immutable_cons(sc, slot, G_NIL));
    sc->returnValue = G_TRUE;
    return pop_callstack(sc);
}

// (add-method Circle (draw *this*))
Cell* op_add_method(Scheme *sc) {
    char *className = first(sc->args)->_name;
    Cell* methodSignature = second(sc->args);
    char *methodName = first(methodSignature);
    Cell* class = lookup_variable_value(sc, sc->globalEnv, className);
    if (type_of(sc, class, EXCEPTION_TYPE)) {
        sc->returnValue = class;
        return pop_callstack(sc);
    }

    int location = hash_fn(methodName, class->_longValue);
    Cell* item = get_vector_item(class, location);
    Cell* slot = NULL;
    for (; item != G_NIL; item = cdr(item)) {
        slot = car(item);
        if (strcmp(car(slot)->_name, methodName) == 0) {
            sc->returnValue = make_default_exception(sc, "PROPERTY_OR_METHOD_EXISTED", "Property or method already existed!");
            return pop_callstack(sc);
        }
    }

    Cell* variable = make_variable(sc, methodName);
    Cell* methodDef = immutable_cons(sc, methodSignature, G_NIL); // init method definition as nil
    slot = immutable_cons(sc, variable, methodDef);
    set_vector_item(class, location, immutable_cons(sc, slot, G_NIL));
    sc->returnValue = G_TRUE;
    return pop_callstack(sc);
}

// (define-method Circle area (* *PI* radius radius))
Cell* op_define_method(Scheme *sc) {
    char *className = first(sc->args)->_name;
    char *methodName = second(sc->args)->_name;
    Cell* methodBody = third(sc->args);
    Cell* class = lookup_variable_value(sc, sc->globalEnv, className);
    if (type_of(sc, class, EXCEPTION_TYPE)) {
        sc->returnValue = class;
        return pop_callstack(sc);
    }

    int location = hash_fn(methodName, class->_longValue);
    Cell* item = get_vector_item(class, location);
    Cell* slot = NULL;
    Cell* methodClosure;
    Cell* methodSignature;
    Cell* methodArg;
    for (; item != G_NIL; item = cdr(item)) {
        slot = car(item);
        if (strcmp(car(slot)->_name, methodName) == 0) {
            methodSignature = cdr(item);
            methodArg = cdr(methodSignature);
            methodClosure = cons(sc, cell_lambda, cons(sc, methodArg, methodBody));
            CDR(slot) = methodClosure;
            sc->returnValue = G_TRUE;
            return pop_callstack(sc);
        }
    }
    sc->returnValue = make_default_exception(sc, "METHOD_NOT_EXISTED", "Method not existed!");
    return pop_callstack(sc);
}

int inherit_from(Scheme *sc, const char *child, const char *parent) {
	TreeNode *node = tree_find(sc->inheritance, child);
	TreeNode *tnode = node;
	while (tnode != NULL) {
		if (strcmp(parent, tnode->name) == 0)
			return TRUE;
		tnode = tnode->parent;
	}
	return FALSE;
}

// A is a B
int is_a(Scheme *sc, Cell *a, Cell *b) {
	return inherit_from(sc, a->_type, b->_type);
}

// (is-a Circle Shape)
Cell* op_is_a(Scheme *sc) {
    char *subClass = first(sc->args)->_name;
    char *supperClass = second(sc->args)->_name;
    Cell* ret = inherit_from(sc, subClass, supperClass) ? G_TRUE : G_FALSE;
    sc->returnValue = ret;
    return pop_callstack(sc);
}

//////////////////////////

// down casting
// (narrow superclass-instance subclass)
Cell* op_narrow(Scheme *sc) {
	;
	//
	return pop_callstack(sc);
}

Cell* op_get_properties(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}
Cell* op_get_methods(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}
Cell* op_find_property(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}
Cell* op_find_method(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}



/*

// --(make-object circle Circle make-circle 10)
// --(make-object d Double make-double 123.456)
// (make-circle (make-object Circle) 10)

// (make-Object 'ClassName)
Cell* op_make_object(Scheme *sc) {
	Cell* className = first(sc->args);
	Cell *object = make_object(className);
	sc->obj = object;
	return s_return_helper(sc, sc->obj);
}

*/



// (new-instance Circle radius (lex "Integer" "10"))
Cell* op_new_instance(Scheme *sc) {
	//Cell* className = first(sc->args);
	//Cell *object = make_object(className);
	//sc->obj = object;
	//sc->returnValue = sc->obj;

    char *className = first(sc->args)->_name;
    Cell* class = lookup_variable_value(sc, sc->globalEnv, className);
    if (type_of(sc, class, EXCEPTION_TYPE)) {
        sc->returnValue = class;
        return pop_callstack(sc);
    }

    Cell* frame = make_vector(sc, DEFAULT_FRAME_SIZE);
    extend_environment(sc, frame, sc->env);

    Cell* args = cdr(sc->args);
    Cell* propertyName = first(args);
    Cell* propertyValue = second(args);
    for (; propertyName != G_NIL ; args = cddr(args), propertyName = first(args), propertyValue = second(args)) {
        add_binding_to_frame(sc, frame, propertyName->_name, propertyValue, FALSE);
    }


	return pop_callstack(sc);
}

Cell* op_get_class_name(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}

Cell* op_get_superclass_name(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}

Cell* op_get_subclass_names(Scheme *sc) {
	//...
	sc->returnValue = G_TRUE;
	return pop_callstack(sc);
}



void init_class_operators(Scheme *sc) {
	ADD_OPERATOR(sc, "add-property", add_property);
	//ADD_OPERATOR(sc, "delete-property", delete_property);
	ADD_OPERATOR(sc, "add-method", add_method);
	//ADD_OPERATOR(sc, "delete-method", delete_method);
	ADD_OPERATOR(sc, "add-inheritance", add_inheritance);
	//ADD_OPERATOR(sc, "change-inheritance", change_inheritance);
}

